#include "common.h"
#include "memory/cache.h"
#include <stdlib.h>
#include "burst.h"
#include "cpu/reg.h"
#include "memory/tlb.h"

uint32_t dram_read(hwaddr_t, size_t);
void dram_write(hwaddr_t, size_t, uint32_t);
int is_mmio(hwaddr_t);
uint32_t mmio_read(hwaddr_t, size_t, int);
void mmio_write(hwaddr_t, size_t, uint32_t, int);


/* Memory accessing interfaces */

// uint32_t hwaddr_read(hwaddr_t addr, size_t len) {
//  return dram_read(addr, len) & (~0u >> ((4 - len) << 3));
// }

// void hwaddr_write(hwaddr_t addr, size_t len, uint32_t data) {
//  dram_write(addr, len, data);
// }

uint32_t hwaddr_read(hwaddr_t addr, size_t len) {
    int32_t set_l1 = (addr >> block_bits) & (l1_sets - 1);
    int32_t i = l1_read(addr);
    int32_t imm_l1 = (addr & (CACHE_BLOCK_SIZE - 1));
    int8_t tmp [CACHE_BLOCK_SIZE * 2];
    if(imm_l1 + len > CACHE_BLOCK_SIZE) {
        memcpy(tmp, cache1[set_l1][i].block + imm_l1, CACHE_BLOCK_SIZE - imm_l1);                                   // 复制第一个块的内容
        int32_t i_last = l1_read(addr + CACHE_BLOCK_SIZE - imm_l1);
        int32_t set_last = ((addr + CACHE_BLOCK_SIZE - imm_l1) >> block_bits) & (l1_sets - 1);
        memcpy(tmp + CACHE_BLOCK_SIZE - imm_l1, cache1[set_last][i_last].block, len - (CACHE_BLOCK_SIZE - imm_l1));     // 复制剩下的第2个块
    }
    else memcpy(tmp, cache1[set_l1][i].block + imm_l1, len);
    int qwq = 0; uint32_t result = unalign_rw(tmp + qwq, 4) & (~0u >> ((4 - len) << 3));                   //  在nemu/include/macro.h
	return result;
}

void hwaddr_write(hwaddr_t addr, size_t len, uint32_t data) {
    l1_write(addr, len, data);
}

hwaddr_t cmd_page_translate(lnaddr_t addr){
    if(!cpu.cr0.protect_enable || !cpu.cr0.paging) return addr;
    uint32_t dicionary = addr >> 22;
    uint32_t page = (addr >> 12) & 0x3ff;
    uint32_t offset = addr & 0xfff;


    // 页目录基地址 + 页目录号 * 页表项大小
    uint32_t tmp = (cpu.cr3.page_directory_base << 12) + dicionary * 4;
    Page_info dictionary_, page_;
    dictionary_.val = hwaddr_read(tmp, 4);
    // 二级页表基地址 + 页号 + 页表项大小
    tmp = (dictionary_.addr << 12) + page * 4;
    page_.val = hwaddr_read(tmp, 4);
    if(dictionary_.p != 1){
        printf("dictionary present != 1!\n");
        return 0;
    }
    if(page_.p != 1){
        printf("second page table present != 1!\n");
        return 0;
    }
    return (page_.addr << 12) + offset;
}

//线性地址->物理地址
hwaddr_t page_translate(lnaddr_t addr){
    if(!cpu.cr0.protect_enable || !cpu.cr0.paging) return addr;
    uint32_t dicionary = addr >> 22;
    uint32_t page = (addr >> 12) & 0x3ff;
    uint32_t offset = addr & 0xfff;

    int index = read_tlb(addr);
    if(index != -1) return ((tlb[index].data << 12) + offset);

    // 页目录基地址 + 页目录号 * 页表项大小
    uint32_t tmp = (cpu.cr3.page_directory_base << 12) + dicionary * 4;
    Page_info dictionary_, page_;
    dictionary_.val = hwaddr_read(tmp, 4);
    // 二级页表基地址 + 页号 + 页表项大小
    tmp = (dictionary_.addr << 12) + page * 4;
    page_.val = hwaddr_read(tmp, 4);
#ifdef DEBUG_page_p
	printf("eip:0x%x\taddr 0x%x\n", cpu.eip, addr);
#endif
	Assert(dictionary_.p == 1, "dirctionary present != 1");
	Assert(page_.p == 1, "second page table present != 1");
#ifdef DEBUG_page
	printf("0x%x\n", (page_.addr << 12) + offset);
#endif
	hwaddr_t addr_ = (page_.addr << 12) + offset;
	write_tlb(addr, addr_);
	return addr_;
}



uint32_t lnaddr_read(lnaddr_t addr, size_t len) {
    //return hwaddr_read(addr, len);
    assert(len == 1 || len == 2 || len == 4);
    uint32_t offset = addr & 0xfff;
    //跨页
    if((int64_t)(offset + len) > 0x1000){
        size_t l = 0xfff - offset + 1;
        uint32_t down_val = lnaddr_read(addr, l);//低位
        uint32_t up_val = lnaddr_read(addr + l, len - 1);//高位
        return (up_val << (l * 8)) | down_val;
    }
    else{
        hwaddr_t hwaddr = page_translate(addr);
        return hwaddr_read(hwaddr, len);
    }
}

void lnaddr_write(lnaddr_t addr, size_t len, uint32_t data) {
    //hwaddr_write(addr, len, data);
    	assert(len == 1 || len == 2 || len ==4);
	uint32_t offset = addr & 0xfff;
	if((int64_t)(offset + len) > 0x1000) {	// 跨页
		// assert(0);
		size_t l = 0xfff - offset + 1;		// 低位最多几个字节同页
		lnaddr_write(addr, l, data & ((1 << (l * 8)) - 1) );			// 低位
		lnaddr_write(addr + l, len - l, data >> (l * 8));				//高位
	}
	else {
		hwaddr_t hwaddr = page_translate(addr);
		hwaddr_write(hwaddr, len, data);
	}
}

//下面是VM
lnaddr_t seg_translate(swaddr_t addr, size_t len, uint8_t sreg){
    if(cpu.cr0.protect_enable == 0) return addr;
    else return cpu.sreg[sreg].base + addr;
}

uint32_t swaddr_read(swaddr_t addr, size_t len, uint8_t sreg) {
#ifdef DEBUG
    assert(len == 1 || len == 2 || len == 4);
#endif
    lnaddr_t lnaddr = seg_translate(addr, len, sreg);
	return lnaddr_read(lnaddr, len);
}

void swaddr_write(swaddr_t addr, size_t len, uint32_t data, uint8_t sreg) {
#ifdef DEBUG
    assert(len == 1 || len == 2 || len == 4);
#endif
    lnaddr_t lnaddr = seg_translate(addr, len, sreg);
	lnaddr_write(lnaddr, len, data);
}

